
/**
 * IndexedDB操作类
 * 访问：indexdb与 web storage 一致，均是在创建数据库的域名下才能访问。且不能指定访问域名。
    存储时间：存储时间永久，除非用户清除数据，可以用作长效的存储。

    大小限制：理论上讲，这种存储的方式是没有大小限制的。然而IndexDB的数据库超过50M的时候浏览器会弹出确认。
    基本上也相当于没有限制了。

    性能测试：indexeddb查询少量数据花费差不多20ms左右。大量数据的情况下，相对耗时会变长一些，但是也就在30MS左右，
    也是相当给力了，10W数据+l。

    特点异步。它的数据不是保存在表中，而是保存在对象存储空间中。创建对象存储空间时，需要定义一个键，然后就可以添加数据。
    可以使用游标在对象存储空间中查询特定的对象。而索引则是为了提高查询速度而基于特定的属性创建的。
    说明：indexDB 目前兼容性还不是很好
 */

// 版本主要用来控制数据库的结构，当数据库结构（表结构）发生变化时，版本也会变化
var version = 1;
var dbName = "rayData";
var request;

// 全局 IndexedDB 数据库实例
var db;

/**
 * 打开数据库，并创建表
 * 操作之前执行
 * @param tabName 创建数据表名
 * @param success 打开成功回调，执行后续操作
 * @param fail 打开失败回调
 * 示例
 * dbHelper.dbOpenCrateTable("t_user",
            success => {
            },
        );
 * */
export function dbOpenCrateTable (tabName, success, fail) {
    let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    if (!indexedDB) {
        console.log("浏览器不支持indexedDB");
    }
    request = indexedDB.open(dbName, version);
    // 如果指定的版本号，大于数据库的实际版本号，就会发生数据库升级事件upgradeneeded
    request.onupgradeneeded = (event) => {
        db = event.target.result;
        if (!db.objectStoreNames.contains(tabName)) {
            // 创建数据表并设置id为自增主键
            db.createObjectStore(tabName, {
                "keyPath": "id", // 主键
                "autoIncrement": true // 自增（每加一条数据，主键会自动增长，无需开发者指定）
            });
        }
    }
    // onsuccess 在连接成功后触发
    request.onsuccess = (event) => {
        db = request.result;
        console.log("db connected");
        if (success) {
            success("db connected");
        }
    };

    // onblocked 在连接被阻止的时候触发，比如打开版本低于当前存在的版本
    request.onblocked = (event) => {
        console.log("db request blocked!");
        if (fail) {
            fail("db request blocked!");
        }
    };

    // onerror 在连接失败时触发
    request.onerror =  (event) => {
        console.log("error!", event);
        if (fail) {
            fail(event.target.error || "error!");
        }
    };
}

/**
 * 打开数据库，并创建表
 * 操作之前执行
 * @param tabName 创建数据表名
 * @param indexString 索引名，如classNameAddGradeName
 * @param keys 索引值列表，如['className','gradeName']
 * @param success 打开成功回调，执行后续操作
 * @param fail 打开失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
            },
        );
 * */
export function dbOpenCrateTableWithIndex (tabName, indexString, keys, success, fail) {
    let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    if (!indexedDB) {
        console.log("浏览器不支持indexedDB");
    }
    request = indexedDB.open(dbName, version);
    // 如果指定的版本号，大于数据库的实际版本号，就会发生数据库升级事件upgradeneeded
    request.onupgradeneeded = (event) => {
        db = event.target.result;
        let storeObj;
        if (!db.objectStoreNames.contains(tabName)) {
            // 创建数据表并设置id为自增主键
            storeObj = db.createObjectStore(tabName, {
                "keyPath": "id", // 主键
                "autoIncrement": true // 自增（每加一条数据，主键会自动增长，无需开发者指定）
            });
        } else {
            storeObj = event.target.transaction.objectStore(tabName);
        }
        if (indexString && !storeObj.indexNames.contains(indexString)) {
            storeObj.createIndex(indexString, keys, {unique: false});
        }
    }
    // onsuccess 在连接成功后触发
    request.onsuccess = (event) => {
        db = request.result;
        console.log("db connected");
        if (success) {
            success("db connected");
        }
    };

    // onblocked 在连接被阻止的时候触发，比如打开版本低于当前存在的版本
    request.onblocked = (event) => {
        console.log("db request blocked!");
        if (fail) {
            fail("db request blocked!");
        }
    };

    // onerror 在连接失败时触发
    request.onerror =  (event) => {
        console.log("error!", event);
        if (fail) {
            fail(event.target.error || "error!");
        }
    };
}

/**
 * 插入数据到表中
 * dbOpen成功后执行
 * @param tabName 创建数据表名
 * @param obj 数据实体，建议创建统一的实体传入，保证数据一致性
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
                dbHelper.tableInsert("t_user", {name: "test1", age: "33", address: "测试地址"});
            },
        );
 * */
export function tableInsert (tabName, obj, success, fail) {
    request = db.transaction([tabName], 'readwrite')
        .objectStore(tabName)
        .add(obj);

    // onsuccess 在插入成功后触发
    request.onsuccess = (event) => {
        db = request.result;
        if (request.result) {
            console.log(`${tabName}插入成功`, request.result);
            if (success) {
                success(`${tabName}插入成功`);
            }
        } else {
            console.log(`${tabName}插入失败0`);
            if (fail) {
                fail(event.target.error || `${tabName}插入失败`);
            }
        }
    };

    // onerror 在插入失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}插入失败`);
        if (fail) {
            fail(event.target.error || `${tabName}插入失败`);
        }
    };
}

/**
 * 插入多条数据到表中
 * dbOpen成功后执行
 * @param tabName 创建数据表名
 * @param arr 数据实体列表，建议创建统一的实体传入，保证数据一致性
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
                dbHelper.tableInserts("t_user",
                    [
                        {name: "test6", address: "测试地3", email: "test3@125.com"},
                        {name: "test7", address: "测试地3", email: "test3@125.com"},
                        {name: "test8", address: "测试地3", email: "test3@125.com"},
                        {name: "test9", address: "测试地3", email: "test3@125.com"},
                    ]
                );
            },
        );
 * */
export function tableInserts (tabName, arr, success, fail) {
    let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    if (!indexedDB) {
        console.log("浏览器不支持indexedDB");
    }
    // 打开我们的数据库,使用open方法
    request = indexedDB.open(dbName);

    // onsuccess 在插入成功后触发
    request.onsuccess = (event) => {

        db = event.target.result;
        var transaction = db.transaction([tabName], 'readwrite');
        var objectStore = transaction.objectStore(tabName);
        for (let i of arr) {
            objectStore.add(i);
        }
        if (request.result) {
            console.log(`${tabName}插入成功`, request.result);
            if (success) {
                success(`${tabName}插入成功`);
            }
        } else {
            console.log(`${tabName}插入失败0`);
            if (fail) {
                fail(event.target.error || `${tabName}插入失败`);
            }
        }
    };

    // onerror 在插入失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}插入失败`);
        if (fail) {
            fail(event.target.error || `${tabName}插入失败`);
        }
    };
}

/**
 * 更新数据到表中
 * dbOpen成功后执行
 * @param tabName 创建数据表名
 * @param obj 数据实体，建议创建统一的实体传入，保证数据一致性
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
                dbHelper.tableUpdate("t_user", {id: 2,name: "test3", address: "更新测试地3", email: "test3@125.com"});
            },
        );
 * */
export function tableUpdate (tabName, obj, success, fail) {
    request = db.transaction([tabName], 'readwrite')
        .objectStore(tabName)
        .put(obj);

    // onsuccess 在插入成功后触发
    request.onsuccess = (event) => {
        db = request.result;
        if (request.result) {
            console.log(`${tabName}更新成功`, request.result);
            if (success) {
                success(`${tabName}更新成功`);
            }
        } else {
            console.log(`${tabName}更新失败0`);
            if (fail) {
                fail(event.target.error || `${tabName}更新失败`);
            }
        }
    };

    // onerror 在更新失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}更新失败`);
        if (fail) {
            fail(event.target.error || `${tabName}更新失败`);
        }
    };
}

/**
 * 更新多条数据到表中
 * dbOpen成功后执行
 * @param tabName 创建数据表名
 * @param arr 数据实体列表，建议创建统一的实体传入，保证数据一致性
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例，参考多条数据插入
 * */
export function tableUpdates (tabName, arr, success, fail) {
    let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    if (!indexedDB) {
        console.log("浏览器不支持indexedDB");
    }
    // 打开我们的数据库,使用open方法
    request = indexedDB.open(dbName);

    // onsuccess 在插入成功后触发
    request.onsuccess = (event) => {

        db = event.target.result;
        var transaction = db.transaction([tabName], 'readwrite');
        var objectStore = transaction.objectStore(tabName);
        for (let i of arr) {
            objectStore.put(i);
        }
        if (request.result) {
            console.log(`${tabName}更新成功`, request.result);
            if (success) {
                success(`${tabName}更新成功`);
            }
        } else {
            console.log(`${tabName}更新失败0`);
            if (fail) {
                fail(event.target.error || `${tabName}更新失败`);
            }
        }
    };

    // onerror 在插入失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}更新失败`);
        if (fail) {
            fail(event.target.error || `${tabName}更新失败`);
        }
    };
}

/**
 * 根据id删除数据
 * dbOpen成功后执行
 * @param tabName 创建数据表名
 * @param id 数据id
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
                dbHelper.tableDelete("t_user", 5);
            },
        );
 * */
export function tableDelete (tabName, id, success, fail) {
    request = db.transaction([tabName], 'readwrite')
        .objectStore(tabName)
        .delete(id);

    // onsuccess 在删除成功后触发
    request.onsuccess = (event) => {
        db = request.result;
        console.log(`${tabName}删除成功`, request.result);
        if (success) {
            success(`${tabName}删除成功`);
        }
    };

    // onerror 在删除失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}删除失败`);
        if (fail) {
            fail(event.target.error || `${tabName}删除失败`);
        }
    };
}

/**
 * 根据id删除数据
 * dbOpen成功后执行
 * @param tabName 创建数据表名
 * @param ids 数据id数组
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
                dbHelper.tableDeletes("t_user", [51, 52, 53, 54, 55, 56, 57, 58, 59, 70]);
            },
        );
 * */
export function tableDeletes (tabName, ids, success, fail) {
    let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    if (!indexedDB) {
        console.log("浏览器不支持indexedDB");
    }
    // 打开我们的数据库,使用open方法
    request = indexedDB.open(dbName);

    // onsuccess 在插入成功后触发
    request.onsuccess = (event) => {

        db = event.target.result;
        var transaction = db.transaction([tabName], 'readwrite');
        var objectStore = transaction.objectStore(tabName);
        for (let id of ids) {
            objectStore.delete(id);
        }
        if (request.result) {
            console.log(`${tabName}删除成功`, request.result);
            if (success) {
                success(`${tabName}删除成功`);
            }
        } else {
            console.log(`${tabName}删除失败0`);
            if (fail) {
                fail(event.target.error || `${tabName}删除失败`);
            }
        }
    };

    // onerror 在插入失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}删除失败`);
        if (fail) {
            fail(event.target.error || `${tabName}删除失败`);
        }
    };
}

/**
 * 数据查询
 * dbOpen成功后执行
 * @param tabName 数据表名
 * @param page 页码
 * @param pageSize 每页条数
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
                dbHelper.tableQuery("t_user", 1, 10);
            },
        );
 * */
export function tableQuery (tabName, page, pageSize, success, fail) {
    let data = [];
    let store = db.transaction([tabName], 'readonly').objectStore(tabName);
    request = store.openCursor();
    let count = store.count();
    let index = null;
    request.onsuccess = function (event) {
        let res = event.target.result;
        if (res) {
            if (index === pageSize - 1) {
                data.push(res.value);
                console.log('读取数据成功：', data);
                console.log('总条目', count.result);
                if (success) {
                    success({
                        total: count.result,
                        data: data,
                    });
                }
                return;
            }
            if (index == null && page != 1) {
                console.log('读取跳过：', (page - 1) * pageSize);
                index = 0;
                res.advance((page - 1) * pageSize);
            } else {
                index ++;
                data.push(res.value);
                res.continue();
            }
        } else {
            console.log('读取数据成功：', data);
            console.log('总条目', count.result);
            if (success) {
                success({
                    total: count.result,
                    data: data,
                });
            }
        }
    }

    // onerror 在失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}读取数据失败`);
        if (fail) {
            fail(event.target.error || `${tabName}读取数据失败`);
        }
    };
}

/**
 * 索引数据查询，需使用tableCreateWithIndex方法创建对应的索引
 * dbOpen成功后执行
 * @param tabName 数据表名
 * @param indexString 索引名，如classNameAddGradeName
 * @param keys 索引值对应的值列表，如['className','gradeName']
 * @param page 页码
 * @param pageSize 每页条数
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
                dbHelper.tableQueryWithIndex("t_user", "nameValue", ["test3"], 1, 10);
            },
        );
 * */
export function tableQueryWithIndex (tabName, indexString, vals, page, pageSize, success, fail) {
    let data = [];
    let store = db.transaction([tabName], 'readonly').objectStore(tabName);
    request = store.index(indexString).openCursor(vals);
    // 此处查询总数存在问题
    // let count = store.count();
    let index = null;
    request.onsuccess = function (event) {
        let res = event.target.result;
        if (res) {
            if (index === pageSize - 1) {
                data.push(res.value);
                console.log('读取数据成功：', data);
                console.log('总条目', data ? data.length : 0);
                if (success) {
                    success({
                        total: data ? data.length : 0,
                        data: data,
                    });
                }
                return;
            }
            if (index == null && page != 1) {
                console.log('读取跳过：', (page - 1) * pageSize);
                index = 0;
                res.advance((page - 1) * pageSize);
            } else {
                index ++;
                data.push(res.value);
                res.continue();
            }
        } else {
            console.log('读取数据成功：', data);
            console.log('总条目', data ? data.length : 0);
            if (success) {
                success({
                    total: data ? data.length : 0,
                    data: data,
                });
            }
        }
    }

    // onerror 在失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}读取数据失败`);
        if (fail) {
            fail(event.target.error || `${tabName}读取数据失败`);
        }
    };
}

/**
 * 清空表数据
 * dbOpen成功后执行
 * @param tabName 创建数据表名
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * 示例
 * dbHelper.dbOpenCrateTableWithIndex("t_user", "nameValue", ["name"],
            success => {
                dbHelper.tableClear("t_user");
            },
        );
 * */
export function tableClear (tabName, success, fail) {
    request = db.transaction([tabName], 'readwrite')
        .objectStore(tabName)
        .clear();

    // onsuccess 在插入成功后触发
    request.onsuccess = (event) => {
        db = request.result;
        console.log(`${tabName}清空成功`, request.result);
        if (success) {
            success(`${tabName}清空成功`);
        }
    };

    // onerror 在更新失败时触发
    request.onerror = (event) => {
        console.log(`${tabName}清空失败`);
        if (fail) {
            fail(event.target.error || `${tabName}清空失败`);
        }
    };
}

/**
 * 数据库删除
 * dbOpen成功后执行
 * @param success 成功回调，执行后续操作
 * @param fail 失败回调
 * */
export function databaseDelete (success, fail) {
    if (db != null) {
        db.close();
        db = null;
    }
    let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
    if (!indexedDB) {
        console.log("浏览器不支持indexedDB");
    }
    indexedDB.deleteDatabase(dbName);
}

export default {
    dbOpenCrateTable,
    dbOpenCrateTableWithIndex,
    tableInsert,
    tableUpdate,
    tableUpdates,
    tableInserts,
    tableDelete,
    tableDeletes,
    tableQuery,
    tableQueryWithIndex,
    tableClear,
    databaseDelete,
}